/*
 AcessOS v0.1
 Process Sceduler/Controller
 * proc.c
*/
#include <system.h>
#include <proc.h>
#include <proc_int.h>

#define PROC_DEBUG	0
#define DEBUG	(PROC_DEBUG | ACESS_DEBUG)

// === CONSTANTS ===
// --- Debugging Options
#define USE_SAFE_DELAY		1	// Use a safe (but inefficent) version of `Proc_Delay`
#define	SCHED_SANITY_CHECK	0	// Perform sanity checks in `sched`
// --- General Options
#define	STACK_BTM	0xA0000000	// Lowest User Stack
#define	STACK_TOP	0xB0000000	// Highest User Stack Top
#define TLS_PAGE0	0x00001000	// First TLS Page (Lower addresses are unused by 32-bit apps)
#define STACK_SIZE	16		// # of Pages preallocated in a user stack = 64KiB
#define STACK_COUNT	((STACK_TOP-STACK_BTM)/(STACK_SIZE<<12))	// Number of user stacks
#define	MAGIC_SWITCH	0x1337ACE9	// Magic Number for process switch
#define PROC_DEF_PRI	5	// Default Quantum of processes
// --- Macro Defined Constants
#define T_PROC_NEXT_OFS	((int)&(((t_proc*)NULL)->next))	// Offset of `next` in a process (for LL functions)
#define	MAGIC_SWITCH_S	"$"STR(MAGIC_SWITCH)	// String version of switch magic

// === IMPORTS ===
extern Uint	giCurrentCR3;	// Used to get the PID 0 CR3 Value
extern int	sys_stack;
extern void	Proc_Stack_Top;
extern void	sched();
extern Uint	getEip();
extern Uint	getEipSafe(Uint *esp);
extern void Proc_DropToUsermode();
extern void Proc_ProcessStart(Uint *stack);
extern void User_ReturnExit();		// Usermode Return
extern void time_handler(struct regs *r);
extern void	LDT_Create(Uint TLS1Base);
extern void	LDT_Set(int id, Uint Base, Uint Limit, Uint16 Flags);

// === PROTOTYPES ===
void	tss_setup();
void	Proc_Exit();
 int	Proc_Kill(int pid);
void	Proc_DumpRunning();
Uint	Proc_MakeUserStack();
Uint	Proc_CloneKernelStack(Uint *OldBase);

// === GLOBALS ===
t_proc_tss	proc_tss[MAX_CPUS];
 int	proc_pid = 0;	// Process ID
 int	proc_tid = 0;	// Thread ID
 int	proc_nextTid = 1;	// Next TID (Also the next PID)
t_proc	*gpCurrentProc = NULL;
t_proc	*gpProcsActive = NULL;
t_proc	*gpProcsAsleep = NULL;
t_procWait	*gpProcsWaiting = NULL;

// === CODE ===
/**
 \fn void Proc_Install()
 \brief Installs process handler
 - Initialise kernel process
 - Install TSS
 - Installs scheduler to IRQ0 (timer)
*/
void Proc_Install()
{
	gpCurrentProc = malloc(sizeof(t_proc));
	gpCurrentProc->status = PROCSTAT_ACTIVE;
	gpCurrentProc->tid = 0;
	gpCurrentProc->pid = 0;
	gpCurrentProc->ppid = 0;
	gpCurrentProc->slots =
		gpCurrentProc->timeRem = PROC_DEF_PRI;
	gpCurrentProc->endBss = 0;
	gpCurrentProc->next = NULL;
	gpCurrentProc->esp =
		gpCurrentProc->ebp =
		gpCurrentProc->eip = 0;
	gpCurrentProc->cr3 = giCurrentCR3;
	gpCurrentProc->uid = 0;	// Root
	gpCurrentProc->gid = 0;
	gpCurrentProc->ImageName = "KERNEL";
	gpCurrentProc->Messages = NULL;
	gpCurrentProc->Spinlock = 0;
	gpCurrentProc->KernelStack = 0xF0000000-4;
	gpProcsActive = gpCurrentProc;
	LDT_Install();
	// Flush TSS
	__asm__ __volatile__ ("ltr %%ax": : "a" ((6<<3)/*|3*/));
	// Set Scheduler Handler
    IDT_SetGate(IRQ_OFFSET+0, (Uint)sched, 0x08, 0x8E);
}

/**
 * \fn int Proc_Fork()
 * \brief Clone the current process
 * \return -1 on error, 0 for child and PID for parent
*/
int Proc_Fork()
{
	 int	tid;
	Uint	eip;
	t_proc	*proc, *tmpproc;
	
	//#if DEBUG
	//	LogF("Proc_Fork: ()\n");
	//#endif
	
	// = Clear Interrupts =
	__asm__ __volatile__ ("cli");
	
	// Get next PID
	tid = proc_nextTid++;
	
	proc = malloc(sizeof(t_proc));
	
	// === Clone Page Directory ===
	proc->cr3 = MM_CloneCurrent();
	
	// === Fill Process Structure ===
	proc->slots = proc->timeRem = PROC_DEF_PRI;
	proc->endBss = gpCurrentProc->endBss;
	proc->pid = tid;
	proc->tid = tid;
	proc->status = PROCSTAT_ACTIVE;
	proc->ppid = proc_pid;
	proc->esp = proc->ebp = 0;
	proc->next = NULL;
	proc->uid = gpCurrentProc->uid;
	proc->gid = gpCurrentProc->gid;
	proc->Messages = NULL;
	proc->Spinlock = 0;
	proc->KernelStack = gpCurrentProc->KernelStack;
	//proc->ImageName = "CHILD";
	proc->ImageName = malloc( strlen(gpCurrentProc->ImageName) + 1 );
	strcpy(proc->ImageName, gpCurrentProc->ImageName);
	
	// Add to task list
	tmpproc = gpProcsActive;
	while(tmpproc->next)	tmpproc = tmpproc->next;
	tmpproc->next = proc;
	
	// = Read ESP and EBP
	__asm__ __volatile__ ("mov %%esp, %0" : "=r" (proc->esp) );
	__asm__ __volatile__ ("mov %%ebp, %0" : "=r" (proc->ebp) );
	eip = getEip();
	// Check if is child
	if(eip == MAGIC_SWITCH) {
		outportb(0x20, 0x20);
		return 0;
	}
	proc->eip = eip;
	//#if DEBUG
	//	LogF(" Proc_Fork: Parent\n");
	//#endif
	
	__asm__ __volatile__ ("sti");
	// = Return Child PID =
	return tid;
}

/*
int Proc_KFork()
- Creates a process that is completely empty save for
  kernel data/code
*/
int Proc_KFork()
{
	 int	tid;
	Uint	eip;
	t_proc	*proc, *tmpproc;
	
	if(gpCurrentProc == NULL) {
		warning("Proc_KFork - No Current Process (Has Multitasking Started?)\n");
		return -1;
	}

	// = Clear Interrupts =
	__asm__ __volatile__ ("cli");
	
	tid = proc_nextTid++;
	
	proc = malloc(sizeof(t_proc));
	
	// === Clone Page Directory ===
	proc->cr3 = MM_CloneKernel();
	
	// === Fill Process Structure ===
	proc->slots = proc->timeRem = PROC_DEF_PRI;
	proc->endBss = gpCurrentProc->endBss;
	proc->pid = tid;
	proc->tid = tid;
	proc->status = PROCSTAT_ACTIVE;
	proc->ppid = proc_pid;
	proc->esp = proc->ebp = 0;
	proc->next = NULL;
	proc->uid = gpCurrentProc->uid;
	proc->gid = gpCurrentProc->gid;
	proc->ImageName = "NULL";
	proc->Messages = NULL;
	proc->Spinlock = 0;
	proc->KernelStack = gpCurrentProc->KernelStack;
	
	// Add to task list
	tmpproc = gpProcsActive;
	while(tmpproc->next)	tmpproc = tmpproc->next;
	tmpproc->next = proc;
	
	// = Read ESP and EBP
	__asm__ __volatile__ ("mov %%esp, %0" : "=r" (proc->esp) );
	__asm__ __volatile__ ("mov %%ebp, %0" : "=r" (proc->ebp) );
	eip = getEip();
	// Check if is child
	if(eip == MAGIC_SWITCH) {
		outportb(0x20, 0x20);
		return 0;
	}
	proc->eip = eip;
	
	__asm__ __volatile__ ("sti");
	// = Return Child PID =
	return tid;
}

/**
 * \fn int Proc_Clone(void *UserStack)
 * \brief Create a new thread in the current process
 */
int Proc_Clone(void *UserStack)
{
	 int	tid;
	Uint	eip;
	Uint	newesp, curbase;
	t_proc	*proc, *tmpproc;
	
	if(gpCurrentProc == NULL) {
		warning("Proc_KFork - No Current Process (Has Multitasking Started?)\n");
		return -1;
	}

	// = Clear Interrupts =
	__asm__ __volatile__ ("cli");
	
	tid = proc_nextTid++;
	
	// Create new Process
	proc = malloc(sizeof(t_proc));
	memcpyd(proc, gpCurrentProc, sizeof(t_proc)/4);
	proc->cr3 = gpCurrentProc->cr3;
	
	// === Fill Process Structure ===
	proc->slots = proc->timeRem;
	proc->tid = tid;
	proc->esp = proc->ebp = 0;
	proc->next = NULL;
	proc->Messages = NULL;
	proc->Spinlock = 0;
	
	// Add to task list
	tmpproc = gpProcsActive;
	while(tmpproc->next)	tmpproc = tmpproc->next;
	tmpproc->next = proc;
	
	// Get new stack
	newesp = Proc_CloneKernelStack(&curbase);
	///\note This assumes that the IRET data is at the base of the stack
	*(Uint*)(newesp-8) = (Uint)UserStack;	// SS is at the top, ESP is below it
	
	// Save base of kernel stack
	proc->KernelStack = newesp;
	
	// = Read ESP and EBP
	__asm__ __volatile__ ("mov %%esp, %0" : "=r" (proc->esp) );
	__asm__ __volatile__ ("mov %%ebp, %0" : "=r" (proc->ebp) );
	// Adjust ESP
	proc->esp += newesp - curbase;
	// Get EIP
	eip = getEip();
	// Check if is child
	if(eip == MAGIC_SWITCH) {
		outportb(0x20, 0x20);
		return 0;
	}
	proc->eip = eip;
	
	__asm__ __volatile__ ("sti");
	// = Return Child Thread ID =
	return tid;
}

/**
 * \fn int Proc_Execve(char *path, char **argv, char **envp, int dpl)
 * \brief Replace the current process image with another
 * \param path	Path to new image
 * \param argv	Arguments
 * \param envp	Environment
 * \param dpl	Permission level to execute at (Valid values are 0 and 3)
 * \return 0 on error, no return on success
 */
int Proc_Execve(char *path, char **argv, char **envp, int dpl)
{
	Uint	ebss;
	 int	i, j;
	 int	argc = 0;
	Uint	iEntryPoint;
	Uint	iArgument;
	Uint	*stacksetup;
	Uint	*newArgv;
	Uint	*argBase;
	Uint16	ss, cs;
	char	*strPtr;
	
	//#if DEBUG
	//	LogF("Proc_Execve: (path='%s',argv=**0x%x,envp=**0x%x,dpl=%i)\n", path, argv, envp, dpl);
	//#endif
	
	if( IsHeap( gpCurrentProc->ImageName ) ) {
		free( gpCurrentProc->ImageName );
	}
	// Set Image Name
	gpCurrentProc->ImageName = (char*) malloc( strlen(path)+1 );
	strcpy(gpCurrentProc->ImageName, path);
	// Set Image End
	gpCurrentProc->endBss = 0;
	
	// == Cache argv and envp ==
	/// \todo Fix this code to properly parse and cache data
	j = 0;	// Total Data Length
	strPtr = 0;	// Array Lengths
	// Get size of Arguments
	if(argv) {
		// Strings
		for(i=0;argv[i];i++)	j += strlen(argv[i])+1;
		// Inicies
		j += sizeof(Uint)*(i+1);
		strPtr += sizeof(Uint)*(i+1);
		argc = i;	// Argument Count
	}
	// Get size of Environment
	if(envp) {
		// Strings
		for(i=0;envp[i];i++)	j += strlen(envp[i])+1;
		// Indicies
		j += sizeof(Uint)*(i+1);
		strPtr += sizeof(Uint)*(i+1);
	}
	// Allocate Space
	newArgv = malloc(j);
	if(newArgv == NULL) {
		warning("Proc_Execve - Unable to allocate temporary argument/environment buffer (%i bytes)\n", j);
		return 0;
	}
	// Re-initalise variables
	strPtr += (Uint)newArgv;	// String Data Start
	j = 0;
	// Save Arguments
	if(argv) {
		for(i=0;argv[i];i++)
		{
			newArgv[j++] = (Uint)strPtr;
			strcpy(strPtr, argv[i]);
			strPtr += strlen(argv[i]) + 1;
		}
	}
	newArgv[j++] = 0;	// NULL Terminator
	// Save Environment
	if(envp) {
		for(i=0;envp[i];i++)
		{
			newArgv[j++] = (Uint)strPtr;
			strcpy(strPtr, envp[i]);
			strPtr += strlen(envp[i]) + 1;
		}
	}
	newArgv[j++] = 0;	// NULL Terminator
	
	// == Clear Memory Space
	MM_Clear();
	LDT_Clear();
	
	// = Load Binary =
	iArgument = Binary_Load(gpCurrentProc->ImageName, &iEntryPoint);
	if(iArgument == 0) {
		warning("Unable to load image '%s'\n", gpCurrentProc->ImageName);
		Proc_Exit();
		return 0;
	}
	
	// = Find the end of the program image =
	for(ebss=0xA000000;ebss;ebss-=0x1000)
	{
		if( MM_IsValid(ebss) )		break;
	}
	ebss += 0x1000;	// Reverse previous increment
	gpCurrentProc->endBss = ebss;
	
	// = Allocate User Stack =
	//MM_DumpEx(0, 768);
	stacksetup = (Uint *)Proc_MakeUserStack();
	// Sanity Check Return
	if(!stacksetup) {
		warning("Proc_Execve - No space for user's stack\n");
		Proc_Exit();
		return 0;
	}
	
	// Calculate space taken by arguments (dword aligned)
	i = ((Uint)strPtr - (Uint)newArgv + 3) / 4;
	stacksetup -= i ;	// Make space for arguments
	argBase = stacksetup;
	memcpyd(stacksetup, newArgv, i);
	free(newArgv);		// `newArgv` will still be used as a reference pointer
	
	// Fix Argument Strings
	for(j=0; argBase[j]; j++)
	{
		argBase[j] -= (Uint)newArgv;	// Get relative offset
		argBase[j] += (Uint)argBase;	// And set to new (stack) address
	}
	// Fix Environment Strings
	for(j++; argBase[j]; j++)
	{
		argBase[j] -= (Uint)newArgv;	// Get relative offset
		argBase[j] += (Uint)argBase;	// And set to new (stack) address
	}
	
	// == Get Descriptors ==
	switch(dpl&3)
	{
	case 0:		ss = 0x10;	cs = 0x08;	break;
	default:	ss = 0x23;	cs = 0x1B;	break;
	}

	//Command Line Arguments & Environment
	*--stacksetup = (Uint)argBase + (argc+1)*sizeof(Uint);	// envp
	*--stacksetup = (Uint)argBase;	// argv
	*--stacksetup = argc;		// argc
	*--stacksetup = iArgument;	// Interpreter Argument
	*--stacksetup = (Uint)User_ReturnExit;	// User Return
	
	//IRET
	ebss = (Uint)stacksetup;
	*--stacksetup = ss;		//Stack Segment
	*--stacksetup = ebss;	//Stack Pointer
	*--stacksetup = 0x0202;	//EFLAGS (Resvd (0x2) and IF (0x20))
	*--stacksetup = cs;		//Code Segment
	*--stacksetup = iEntryPoint;	//EIP
	//PUSHAD
	*--stacksetup = 0xAAAAAAAA;	// eax
	*--stacksetup = 0xCCCCCCCC;	// ecx
	*--stacksetup = 0xDDDDDDDD;	// edx
	*--stacksetup = 0xBBBBBBBB;	// ebx
	*--stacksetup = 0xD1D1D1D1;	// edi
	*--stacksetup = 0x54545454;	// esp - NOT POPED
	*--stacksetup = 0x51515151;	// esi
	*--stacksetup = 0xB4B4B4B4;	// ebp
	//Individual PUSHs
	*--stacksetup = ss;	// ds
	*--stacksetup = ss;	// es
	*--stacksetup = ss;	// fs
	*--stacksetup = ss;	// gs
	//*--stacksetup = 0x17;	// gs - Thread Local Storage (First LDT Entry)
	
	//#if DEBUG
	//	LogF(" Proc_Execve: Starting Process - Passing Control to 0x%x\n", iEntryPoint);
	//#endif
	// = Jump to process =
	Proc_ProcessStart(stacksetup);
	
	return 1;
}

/**
 \fn Uint Proc_MakeUserStack()
*/
Uint Proc_MakeUserStack()
{
	 int	j,i;
	Uint	base = STACK_TOP;
	for(j=STACK_COUNT;j--;)
	{
		base -= STACK_SIZE<<12;
		// Check Prospective Space
		for(i=STACK_SIZE;i--;)	if( MM_IsValid(base+(i<<12)) )	break;
		if(i!=-1)	continue;	// If a page was taken go to next space
		// Allocate Stack - Allocate incrementally to clean up MM_Dump output
		for(i=0;i<STACK_SIZE;i++)	mm_alloc(base+(i<<12));
		break;
	}
	if(j==-1)	return 0;
	MM_PagetableFlush();
	//MM_DumpEx(0, 768);
	return base + (STACK_SIZE << 12);
}

/**
 * \fn Uint Proc_CloneKernelStack(Uint *OldBase)
 * \brief Clones the current stack state and repairs the stack frames
 */
Uint Proc_CloneKernelStack(Uint *OldBase)
{
	Uint	base = KSTACK_ADDR;
	Uint	esp, curbase;
	Uint	ebp;
	Uint	*newstack;
	 int	i;
	for(i=KSTACK_NUM;i--;base-=KSTACK_SIZE)
	{
		if(!MM_IsValid(base-0x1000))
			break;
	}
	if(!i)	return 0;
	
	// Allocate new stack
	for(i=0;i<KSTACK_SIZE;i+=0x1000) {
		mm_alloc( base - i - 0x1000 );
	}
	
	// Get stack offset
	__asm__ __volatile__ ("mov %%esp, %%eax":"=a"(esp));
	__asm__ __volatile__ ("mov %%ebp, %%eax":"=a"(ebp));
	curbase = (esp & ~(KSTACK_SIZE-1)) + KSTACK_SIZE;
	esp = curbase - esp;	// Get SP as a delta from the stack base
	
	// Copy Stack data
	memcpyd( (void*)(base-esp), (void*)(curbase-esp), esp/4 );
	
	// Repair stack frames
	ebp += base - curbase;
	newstack = (Uint*)ebp;
	while( curbase - STACK_SIZE < (Uint)newstack
		&& (Uint)newstack < curbase )
	{
		*newstack += base - curbase;	// [EBP] = old EBP
		newstack = (Uint*) *newstack;
	}
	
	if(OldBase)	*OldBase = curbase;
	
	return base;
}

/**
 * \fn int Proc_Open(char *filename, char **argv, char **envp)
 * \brief Open a new process
 * \param filename	Path to new executable
 * \param argv	Arguments
 * \param envp	Environment
 * \return Child PID or -1 on error
 * 
 * Proc_Open calls Proc_Fork() and then Proc_Execve() to create a new image
 * which shares the same environment (file handes etc) as it's parent.
*/
int Proc_Open(char *filename, char **argv, char **envp)
{
	int newPid;
	//#if DEBUG
	//	LogF("Proc_Open: (filename='%s')\n", filename);
	//#endif
	
	// Check if file exists
	newPid = vfs_open(filename, 0);
	if(newPid == -1)
		return -1;
	vfs_close(newPid);
	
	newPid = Proc_Fork();	//!< \todo This should be KFork, but argv and envp need to be saved first
	//newPid = Proc_KFork();
	
	if(newPid == 0)
	{
		Proc_Execve(filename, argv, envp, 3);	//Start in user mode
		Proc_Exit();
		return 1;	//NEVER REACHED
	}
	if(newPid > 0)
		return newPid;
	
	return -1;
}

/**
 * \fn void Proc_Yield()
 * \brief Yield current timeslice
 */
void Proc_Yield()
{
	#if DEBUG >= 2
	LogF("%i (%s) Yielded\n", gpCurrentProc->pid, gpCurrentProc->ImageName);
	#endif
	if(gpCurrentProc)
	{
		gpCurrentProc->timeRem = 0;
		__asm__ __volatile__ ("sti;hlt");	// Halt CPU until IRQ
	}
	else
	{
		__asm__ __volatile__ ("sti;hlt");
	}
}

/**
 \fn void Proc_Delay(int ms)
 \brief Sets a process to delay for a set number of ticks
*/
void Proc_Delay(int ms)
{
	#if USE_SAFE_DELAY
	Uint64	end = unow() + ms;
	while(unow() < end)	Proc_Yield();
	#else
	t_procWait	*ent;
	t_procWait	*new;
	t_proc	*tmpproc;
	int		totalDelay = 0;
	
	ms *= TICKS_PER_MS;
	ms ++;

	// Just use `time_wait` before multitasking is up
	if(gpCurrentProc == NULL)
	{
		warning("Please use time_wait before multitasking is up\n");
		time_wait(ms);
		return;
	}
	
	// Create Delay Entry
	new = malloc(sizeof(t_procWait));
	new->proc = gpCurrentProc;
	if(gpProcsWaiting)
	{
		ent = gpProcsWaiting;
		// Get required slot in wait list
		while(ent->next)
		{
			if(totalDelay + ent->delta > ms)
				break;
			totalDelay += ent->delta;
			ent = ent->next;
		}
		new->delta = ms-totalDelay;
		new->next = ent->next;
		ent->next = new;
		if(new->next)	new->next->delta -= new->delta;
	}
	else
	{
		new->delta = ms;
		new->next = NULL;
		gpProcsWaiting = new;
	}
	
	// Check if this is the top process on active stack
	if(gpProcsActive && gpProcsActive != gpCurrentProc)
	{
		// If Not, walk stack
		tmpproc = gpProcsActive;
		while(tmpproc && tmpproc->next != gpCurrentProc)
			tmpproc = tmpproc->next;
		if(!tmpproc)
			panic("Proc_Delay: Unable to find process on active queue\n");
		tmpproc->next = gpCurrentProc->next;
	}
	else
	{	// Remove from stack
		gpProcsActive = gpCurrentProc->next;
	}
	
	// Yeild Timeslice
	Proc_Yield();
	#endif
}

/**
 \fn void Proc_Sleep()
 \brief Sets a process to sleep until it receives a signal.
*/
void Proc_Sleep()
{
	t_proc	*tmpproc, *thisproc;
	thisproc = gpCurrentProc;
	
	#if DEBUG
	//LogF("Proc_Sleep: PID=%i\n", proc_pid);
	#endif
	
	if(thisproc->status == PROCSTAT_SLEEP)	// This shouldn't happen
		return;
	
	thisproc->status = PROCSTAT_SLEEP;
	
	if(!gpProcsActive)
		panic("Proc_Sleep - Active Stack Empty. ==BUG CATCHER==");
	
	// Check if this is the top process on active stack
	if(gpProcsActive != thisproc)
	{
		// If Not, walk stack
		tmpproc = gpProcsActive;
		while(tmpproc && tmpproc->next != thisproc)
			tmpproc = tmpproc->next;
		if(!tmpproc)
			panic("Proc_Sleep - Unable to find process on active queue\n");
		tmpproc->next = thisproc->next;
	}
	else
	{
		// Remove from stack
		gpProcsActive = thisproc->next;
	}
	
	ll_append((void**)&gpProcsAsleep, T_PROC_NEXT_OFS, thisproc);
	//Add to Sleeping queue
	//tmpproc = gpProcsAsleep;
	//while(tmpproc && tmpproc->next)	tmpproc = tmpproc->next;
	//if(tmpproc)	tmpproc->next = thisproc;
	//else	tmpproc = thisproc;

	#if DEBUG
	//LogF("Proc_Sleep: %i (%s) is napping\n", proc_pid, thisproc->ImageName);
	#endif
	Proc_Yield();
	//__asm__ __volatile__ ("hlt");
}

/**
 \fn int Proc_Kill(int pid)
 \brief Kills the selected process
*/
int Proc_Kill(int pid)
{
	t_proc	*proc, *tmpproc;
	proc = gpProcsActive;
	while(proc && proc->pid != pid)	proc = proc->next;
	if(!proc)
	{
		proc = gpProcsAsleep;
		while(proc && proc->next && proc->next->pid != pid)	proc = proc->next;
		
		tmpproc = proc->next;
		proc->next = tmpproc->next;
		proc = tmpproc;
	}
	if(!proc)
	{
		t_procWait	*pw;
		pw = gpProcsWaiting;
		while(pw && pw->proc->pid != pid)	pw = pw->next;
		if(pw)
			proc = pw->proc;
	}
	if(!proc)
		return -1;
		
	// Mark for death
	proc->status = PROCSTAT_NULL;
	if( IsHeap( gpCurrentProc->ImageName ) ) {
		free( gpCurrentProc->ImageName );
	}
	// Remove Memory Space
	mm_remove(proc->cr3);
	return 1;
}

/**
 \fn void Proc_Exit()
 \brief Kills the current process
*/
void Proc_Exit()
{
	t_proc	*tmpproc, *thisproc;
	thisproc = gpCurrentProc;
	
	// We're messing with the process queue here, so don't allow interupts
	__asm__ __volatile__ ("cli");
	
	// Check if this is the top process on active stack
	if(gpProcsActive && gpProcsActive != thisproc)
	{
		// If Not, walk stackTian van Heerden wrote, "this message came up on the automated parking
		tmpproc = gpProcsActive;
		while(tmpproc && tmpproc->next != thisproc)
			tmpproc = tmpproc->next;
		if(tmpproc)
			tmpproc->next = thisproc->next;
	}
	else
	{
		// Remove from stack
		gpProcsActive = thisproc->next;
	}
	
	// Check if this is the top process on sleeping stack
	if(gpProcsAsleep && gpProcsAsleep != thisproc)
	{
		// If Not, walk stack
		tmpproc = gpProcsAsleep;
		while(tmpproc && tmpproc->next != thisproc)
			tmpproc = tmpproc->next;
		if(tmpproc)
			tmpproc->next = thisproc->next;
	}
	else
	{
		// Remove from stack
		gpProcsAsleep = thisproc->next;
	}
	
	mm_remove(thisproc->cr3);
	
	// Mark for removal
	thisproc->status = PROCSTAT_NULL;
	// Renable Interrupts (If we get stopped here it doesn't matter
	__asm__ __volatile__ ("sti;\n\thlt");
}

/**
 * \fn void Proc_WakeProc(int tid)
 * \brief Wake a process
 */
void Proc_WakeProc(int tid)
{
	t_proc	*proc, *tmpproc;
	
	proc = gpProcsAsleep;
	if(!proc)	return;	//SegFault Protection
	if(proc->tid == tid)	// Check first entry
	{
		proc->status = PROCSTAT_ACTIVE;
		gpProcsAsleep = proc->next;
		ll_append((void**)&gpProcsActive, T_PROC_NEXT_OFS, proc);
		return;
	}
	
	// Search List
	while(proc->next && proc->next->tid != tid)
		proc = proc->next;
	if(!proc->next)	return;
	
	proc->next->status = PROCSTAT_ACTIVE;
	
	tmpproc = gpProcsActive;
	if(!tmpproc) {
		gpProcsActive = proc->next;
		proc->next = proc->next->next;
		return;
	}
	
	while(tmpproc->next)
		tmpproc = tmpproc->next;
	tmpproc->next = proc->next;
	proc->next = proc->next->next;
}

/**
 * \fn int Proc_GetPid()
 * \brief Get Process ID
 */
int Proc_GetPid()
{
	return gpCurrentProc->pid;
}
/**
 * \fn int Proc_GetTid()
 * \brief Get Thread ID
 */
int Proc_GetTid()
{
	return gpCurrentProc->tid;
}
/**
 * \fn int Proc_GetUid()
 * \brief Get User ID
 */
int Proc_GetUid()
{
	return gpCurrentProc->uid;
}
/**
 * \fn int Proc_GetGid()
 * \brief Get Group ID
 */
int Proc_GetGid()
{
	return gpCurrentProc->gid;
}

/**
 * \fn int Proc_WaitPid()
 * \brief Wait for a process to do an action
 */
int	Proc_WaitPid(int pid, int action)
{
	t_proc	*proc;
	
	proc = gpProcsActive;
	while(proc && proc->pid != pid)
		proc = proc->next;
	if(!proc)	return -1;
	
	switch(action)
	{
	case 0:	// Die
		while(proc->pid == pid && proc->status != PROCSTAT_NULL)
			Proc_Yield();
		return 0;
		break;
	}
	return 0;
}

/**
 * \fn int Proc_SendMessage(Uint Dest, Uint Data[4], int Sync)
 * \brief Send a message to a process
 * \note System Call Interface
 */
int Proc_SendMessage(Uint Dest, Uint Data[4], int Sync)
{
	tProc_Message	*msg;
	tProc_Message	*tmpMsg;
	t_proc	*proc;
	
	// Find Target
	proc = gpProcsActive;
	// - Search Active
	while(proc && proc->tid != Dest)	proc = proc->next;
	if(!proc) {
		proc = gpProcsAsleep;
		// - Search Sleeping
		while(proc && proc->tid != Dest)	proc = proc->next;
		if(!proc)	return -1;	// Not Found
	}
	
	// Create Message
	msg = malloc( sizeof(tProc_Message) );
	msg->SourceTid = proc_tid;
	msg->Flags = Sync ? PROC_MSGFLAG_WAKEME : 0;
	msg->Data[0] = Data[0];	msg->Data[1] = Data[1];
	msg->Data[2] = Data[2];	msg->Data[3] = Data[3];
	msg->Next = NULL;
	
	// Append to list
	//LogF(" Proc_SendMessage: proc->Spinlock = %i\n", proc->Spinlock);
	ACQ_LOCK( &proc->Spinlock );
	//LogF(" Proc_SendMessage: proc->Spinlock = %i\n", proc->Spinlock);
	for(tmpMsg = proc->Messages;
		tmpMsg && tmpMsg->Next;
		tmpMsg = tmpMsg->Next
		);
	if(proc->Messages)
		tmpMsg->Next = msg;
	else
		proc->Messages = msg;
	REL_LOCK( &proc->Spinlock );
	//LogF(" Proc_SendMessage: proc->Spinlock = %i\n", proc->Spinlock);
	
	if(proc->status == PROCSTAT_SLEEP)
		Proc_WakeProc(Dest);
	
	if(Sync)
		Proc_Sleep();
	
	//LogF("Proc_SendMessage: RETURN 1\n");
	
	return 1;
}

/**
 * \fn int Proc_GetMessage(Uint *Source, Uint **Data)
 * \brief Check for messages in the queue
 * \param Source	Location to place the sender's ID
 * \param Data		Location to place the message's data
 */
int Proc_GetMessage(Uint *Source, Uint *Data)
{
	tProc_Message	*next;
	
	// Check if list is not empty
	if(!gpCurrentProc->Messages)	return 0;
	
	//LogF("Proc_GetMessage: (Source=%p, Data=%p)\n", Source, Data);
	//LogF(" Proc_GetMessage: gpCurrentProc->Messages = %p\n", gpCurrentProc->Messages);
	
	// Save Data
	if(Source)
		*Source = gpCurrentProc->Messages->SourceTid;
	if(Data) {
		Data[0] = gpCurrentProc->Messages->Data[0];
		Data[1] = gpCurrentProc->Messages->Data[1];
		Data[2] = gpCurrentProc->Messages->Data[2];
		Data[3] = gpCurrentProc->Messages->Data[3];
	}
	
	// Wake Sender (if needed)
	if(gpCurrentProc->Messages->Flags & PROC_MSGFLAG_WAKEME)
		Proc_WakeProc( gpCurrentProc->Messages->SourceTid );
	
	// Remoce from list
	ACQ_LOCK( &gpCurrentProc->Spinlock );
	next = gpCurrentProc->Messages->Next;	// Save Next
	free( gpCurrentProc->Messages );	// Free Message
	gpCurrentProc->Messages = next;		// Shift Message Queue
	REL_LOCK( &gpCurrentProc->Spinlock );
	// Return Success
	//LogF("Proc_GetMessage: RETURN 1\n");
	return 1;
}

/* Extend process memory space
 */
Uint32 proc_brk(Uint32 newEnd)
{
	int increase;
	int	pageDelta;
	Uint32 newAddress;
	
	//LogF("proc_brk: (newEnd = 0x%x)\n", newEnd);
		
	if(newEnd == 0) {
		//LogF("proc_brk: RETURN 0x%x, Current\n", gpCurrentProc->endBss);
		return gpCurrentProc->endBss;
	}
	
	// Calculate Increase in size
	increase = newEnd - gpCurrentProc->endBss;
	//LogF(" proc_brk: increase = %i\n", increase);
	
	if(increase < 0)
	{
		increase -= 0x1000 - (gpCurrentProc->endBss&0xFFF);
		increase = -increase;
		pageDelta = (increase+0xFFF)>>12;
		newAddress = (gpCurrentProc->endBss&0xFFFFF000);
		while(pageDelta--)
		{
			if(!MM_IsValid(newAddress))
				return gpCurrentProc->endBss;
			mm_dealloc(newAddress);
			newAddress -= 0x1000;
		}
		//LogF("proc_brk: RETURN 0x%x, Shrunk\n", newAddress);
		return (gpCurrentProc->endBss = newAddress);
	}
	
	//Within a page
	if(increase+(gpCurrentProc->endBss&0xFFF) <= 0x1000) {
		gpCurrentProc->endBss = newEnd;
		//LogF("proc_brk: RETURN 0x%x, Same Page\n", newEnd);
		return newEnd;
	}

	//Crossing Page Boundaries
	for(
		newAddress = gpCurrentProc->endBss & ~0xFFF;
		newAddress < ((newEnd + 0xFFF) & ~0xFFF);
		newAddress += 0x1000
		)
	{
		if(MM_IsValid(newAddress))
			continue;
		mm_alloc(newAddress);
	}
	
	gpCurrentProc->endBss = newEnd;
	//LogF("proc_brk: RETURN 0x%x, Grown\n", newEnd);
	return newEnd;
}

/**
 \fn void Proc_DumpRunning()
*/
void Proc_DumpRunning()
{
	t_proc	*proc;
	t_procWait	*ent;
	LogF("Active Processes:\n");
	proc = gpProcsActive;
	while(proc)
	{
		if(proc == gpCurrentProc)
			LogF("#");
		else
			LogF(" ");
		LogF(" TID %i: '%s', PID=%i, Parent=%i, UID:%i, GID:%i, CR3=0x%x\n",
			proc->tid, proc->ImageName, proc->pid,
			proc->ppid, proc->uid, proc->gid,
			proc->cr3);
		if(proc == proc->next)
			break;
		proc = proc->next;
	}
	
	LogF("Sleeping Processes:\n");
	proc = gpProcsAsleep;
	while(proc)
	{
		LogF("  TID %i: '%s', Parent=%i, UID:%i, GID:%i\n",
			proc->pid, proc->ImageName, proc->ppid, proc->uid, proc->gid);
		if(proc == proc->next)
			break;
		proc = proc->next;
	}
	
	LogF("Waiting Processes:\n");
	ent = gpProcsWaiting;
	while(ent)
	{
		proc = ent->proc;
		LogF("  TID %i: '%s', Parent=%i, UID:%i, GID:%i, Wait=%i\n",
			proc->pid, proc->ImageName, proc->ppid, proc->uid, proc->gid, ent->delta);
		if(ent == ent->next)
			break;
		ent = ent->next;
	}
	
}

/**
 * \fn void tss_setup()
 * \brief Sets the TSS up for multitasking
 */
void tss_setup()
{
	#if 0
	t_proc_tss	*tss = (void*)0xE0000000;	// Top of User Kernel Data
	
	GDT_Set(5, tss, sizeof(t_proc_tss)-1, 0x89, 0x00);
	#endif
	
	#if 1
	int i = 0;
	Uint32	base;
	
	memset(&proc_tss, 0, sizeof(t_proc_tss)*MAX_CPUS);
	#if MAX_CPUS > 1
	for(i=MAX_CPUS;i--;)
	#endif
	{
		base = ((Uint32)&proc_tss[i]);
		proc_tss[i].ss0 = 0x10;
		proc_tss[i].esp0 = 0xF0000000 - 4;
		//proc_tss[i].esp0 = (Uint)&Proc_Stack_Top - 4;
		//proc_tss[i].ss = 0x10|3;
		//proc_tss[i].cs = 0x08|3;
		//proc_tss[i].ds = proc_tss[i].es = proc_tss[i].gs = proc_tss[i].fs = 0x10|3;
		proc_tss[i].iobitmap = sizeof(t_proc_tss);
		GDT_Set(6+i, base, sizeof(t_proc_tss)-1, 0x89, 0x00);
	}
	#endif
}

/**
 * \fn void proc_scheduler(Uint stack)
 * \brief Called on timer and swaps processes
 * \param stack	ESP on calling (Just passed to time_handler)
*/
void proc_scheduler(Uint stack)
{
	Uint32	eip, esp, ebp;
	
	// NOTE: This should always be first
	// = Read Esp, Ebp, Eip =
	__asm__ __volatile__ ("mov %%esp, %0" : "=r"(esp));
	__asm__ __volatile__ ("mov %%ebp, %0" : "=r"(ebp));
	eip = getEip();
	
	// = Check if returning =
	if(eip == MAGIC_SWITCH)	return;
	
	// Call Timer Handler
	time_handler( (struct regs *)&stack );
	
	#if DEBUG >= 2	// Debug Text
		puts("Scheduler.\n");
	#endif
	
	// === Wake Delayed Processes ===
	// While queue is not empty and delta on first entry is zero
	while(gpProcsWaiting && gpProcsWaiting->delta == 0)
	{
		void	*next;
		#if DEBUG
		LogF("Restore #%i\n", gpProcsWaiting->proc->pid);
		#endif
		// Append to active queue
		gpProcsWaiting->proc->next = gpProcsActive;	// Set Next to current top
		gpProcsActive = gpProcsWaiting->proc;	// Set active to proc
		// Remove from wait queue
		next = gpProcsWaiting->next;	// Save next in list
		free(gpProcsWaiting);	// Free old entry
		gpProcsWaiting = next;	// Set new entry
	}
	if(gpProcsWaiting) {
		#if DEBUG
		LogF("gpProcsWaiting->delta = %i\n", gpProcsWaiting->delta);
		#endif
		gpProcsWaiting->delta --;
	}
	
	// == Check for Idling (No Current Process) ==
	if(!gpCurrentProc)
	{
		// Check if the active queue is empty
		if(!gpProcsActive) {
			// If so, ACK IRQ and halt CPU
			#if DEBUG >= 2
				puts("proc_scheduler: Idling\n");
			#endif
			outportb(0x20, 0x20);
			__asm__ __volatile__ ("sti;hlt" : : );	//Halt the system until next interupt
			return;		// Superflous
		}
	}
	else	// Process currently running
	{
		// -- Check if the current process has time remaining --
		if(gpCurrentProc->timeRem > 0) {
			// Decrement time and return
			gpCurrentProc->timeRem --;
			return;
		}
	
		#if DEBUG >= 2
			puts("Swapping Processes\n");
		#endif
	
		// = Update EIP, ESP, EBP =
		gpCurrentProc->eip = eip;
		gpCurrentProc->esp = esp;
		gpCurrentProc->ebp = ebp;

		// = Check if the current process is about to be killed
		if(gpCurrentProc->status == PROCSTAT_NULL)
		{
			t_proc	*tmpp;
			
			#if DEBUG
			LogF("proc_scheduler: Destroy %i\n", gpCurrentProc->pid);
			#endif

			// Remove From Active Queue
			tmpp = gpProcsActive;
			// Check if process is on the top of the queue
			if(tmpp == gpCurrentProc)
			{
				gpCurrentProc = tmpp->next;
				free(tmpp);
			}
			else
			{
				while(tmpp && tmpp->next != gpCurrentProc)
					tmpp = tmpp->next;
				if(tmpp)
				{
					tmpp->next = gpCurrentProc->next;
					// Wake all processes that are waiting on us
					// --- TODO ---
					// Free
					tmpp = gpCurrentProc->next;
					free(gpCurrentProc);
					gpCurrentProc = tmpp;
				} else {	// Process not found on queue
					gpCurrentProc = NULL;
					//WarningEx("PROC", "Current process is not on active queue.");
				}
			}
		} else {
			// Go to next process
			gpCurrentProc = gpCurrentProc->next;
		}
	}
	
	// = Check for the end of the active queue =
	if(!gpCurrentProc)	gpCurrentProc = gpProcsActive;
	
	// = Check for an Active Process = 
	if(!gpCurrentProc || gpCurrentProc->status != PROCSTAT_ACTIVE)
	{
		#if DEBUG >= 2
		puts("No active processes, Idling\n");
		#endif
		// = Send EOI to Master Controller =
		outportb(0x20, 0x20);
		__asm__ __volatile__ ("sti;hlt" : : );	//Halt the system until next interupt
		return;
	}
	
	// = Update Process Time =
	gpCurrentProc->timeRem = gpCurrentProc->slots;
	#if DEBUG >= 2
	LogF("Starting gpCurrentProc->timeRem = %i\n", gpCurrentProc->timeRem);
	#endif
	
	// = Read Registers =
	eip = gpCurrentProc->eip;
	esp = gpCurrentProc->esp;
	ebp = gpCurrentProc->ebp;
	
	#if SCHED_SANITY_CHECK
	if(eip < 0x1000) {
		warning("PID%i was trashed (EIP (0x%x) < 0x1000)", gpCurrentProc->pid, eip);
		outportb(0x20, 0x20);
		__asm__ __volatile__ ("hlt" : : );
		return;
	}
	#endif
	
	#if DEBUG >= 2
		LogF(" Proc_Sceduler: eip=0x%x,esp=0x%x,ebp=0x%x\n", eip, esp, ebp);
	#endif
	
	// Change Memory Space
	MM_SetCR3( gpCurrentProc->cr3 );
	// Update kernel stack address
	proc_tss[0].esp0 = gpCurrentProc->KernelStack;
	// Set new Process PID
	proc_pid = gpCurrentProc->pid;
	proc_tid = gpCurrentProc->tid;

	//MM_Dump();	// DEBUG

	//Perform Task Switch
	__asm__ __volatile__ (
	"cli;\n\t"		//Clear Interrupts
	"mov %2, %%esp;\n\t"	//Set ESP
	"mov %3, %%ebp;\n\t"	//Set EBP
	"sti;\n\t"		//Start Interrupts
	"jmp *%%ecx" : : "a" (MAGIC_SWITCH), "c" (eip), "r" (esp), "r" (ebp) );
	
	return;
}

// === EXPORTS ===
EXPORT(Proc_Yield);
